package com.itjin.health.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itjin.health.domain.dto.ForgetPasswordStep1Request;
import com.itjin.health.domain.dto.ForgetPasswordStep2Request;
import com.itjin.health.domain.dto.UserLoginRequest;
import com.itjin.health.domain.dto.UserRegisterRequest;
import com.itjin.health.domain.dto.UserUpdateInfoRequest;
import com.itjin.health.domain.entity.User;
import com.itjin.health.domain.entity.InjuryReport;
import com.itjin.health.domain.entity.RehabilitationPlan;
import com.itjin.health.domain.entity.OnlineAssessmentAppointment;
import com.itjin.health.domain.entity.OfflineAssessmentAppointment;
import com.itjin.health.domain.vo.UserStatsVO;
import com.itjin.health.domain.vo.UserVO;
import com.itjin.health.domain.vo.UserComprehensiveInfoVO;
import com.itjin.health.domain.vo.RehabilitationPlanVO;
import com.itjin.health.domain.vo.InjuryReportVO;
import com.itjin.health.exception.BusinessException;
import com.itjin.health.exception.ErrorCode;
import com.itjin.health.mapper.UserMapper;
import com.itjin.health.mapper.InjuryReportMapper;
import com.itjin.health.mapper.RehabilitationPlanMapper;
import com.itjin.health.mapper.OnlineAssessmentAppointmentMapper;
import com.itjin.health.mapper.OfflineAssessmentAppointmentMapper;
import com.itjin.health.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import java.util.List;
import java.time.LocalDate;
import java.util.ArrayList;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

  /**
   * 盐值，混淆密码
   */
  private static final String SALT = "itjin";

  @Resource
  private RedisTemplate redisTemplate;

  @Resource
  private UserMapper userMapper;

  @Resource
  private InjuryReportMapper injuryReportMapper;

  @Resource
  private RehabilitationPlanMapper rehabilitationPlanMapper;

  @Resource
  private OnlineAssessmentAppointmentMapper onlineAssessmentAppointmentMapper;

  @Resource
  private OfflineAssessmentAppointmentMapper offlineAssessmentAppointmentMapper;

  @Override
  public Integer userRegister(UserRegisterRequest userRegisterRequest) {
    String studentNumber = userRegisterRequest.getStudentNumber();
    String username = userRegisterRequest.getUsername();
    String password = userRegisterRequest.getPassword();
    String checkPassword = userRegisterRequest.getCheckPassword();
    String email = userRegisterRequest.getEmail();
    String phone = userRegisterRequest.getPhone();
    String role = userRegisterRequest.getRole();
    String captcha = userRegisterRequest.getCaptcha();

    // 1. 校验
    if (StringUtils.isBlank(studentNumber) || StringUtils.isBlank(username) || StringUtils.isBlank(password)
        || StringUtils.isBlank(checkPassword) || StringUtils.isBlank(role) || StringUtils.isBlank(email)
        || StringUtils.isBlank(captcha)) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
    }

    // 2. 验证邮箱验证码
    if (!verifyCaptcha(email, captcha)) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误或已过期");
    }

    if (studentNumber.length() > 20) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "学号过长");
    }
    if (username.length() > 20) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名过长");
    }
    if (password.length() < 8 || checkPassword.length() < 8) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度不能小于8位");
    }
    // 密码和校验密码相同
    if (!password.equals(checkPassword)) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
    }

    // 3. 学号不能重复
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("student_number", studentNumber);
    long count = this.baseMapper.selectCount(queryWrapper);
    if (count > 0) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "学号已被注册");
    }

    // 4. 邮箱和手机号不能重复（如果提供了）
    if (StringUtils.isNotBlank(email)) {
      queryWrapper = new QueryWrapper<>();
      queryWrapper.eq("email", email);
      count = this.baseMapper.selectCount(queryWrapper);
      if (count > 0) {
        throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱已被注册");
      }
    }

    if (StringUtils.isNotBlank(phone)) {
      queryWrapper = new QueryWrapper<>();
      queryWrapper.eq("phone", phone);
      count = this.baseMapper.selectCount(queryWrapper);
      if (count > 0) {
        throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号已被注册");
      }
    }

    // 5. 加密密码
    String encryptPassword = DigestUtil.md5Hex(SALT + password);

    // 6. 插入数据
    User user = new User();
    user.setStudentNumber(studentNumber);
    user.setUsername(username);
    user.setPassword(encryptPassword);
    user.setEmail(email);
    user.setPhone(phone);
    user.setRole(role);

    // 如果角色是管理员或教师，设置status为2（待审核）
    if ("管理员".equals(role) || "教师".equals(role)) {
      user.setStatus(2); // 待审核状态
    } else {
      user.setStatus(0); // 正常状态
    }

    boolean saveResult = this.save(user);
    if (!saveResult) {
      throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误");
    }

    return user.getId();
  }

  @Override
  public UserVO userLogin(UserLoginRequest userLoginRequest) {
    String studentNumber = userLoginRequest.getStudentNumber();
    String password = userLoginRequest.getPassword();

    // 1. 校验
    if (StringUtils.isBlank(studentNumber) || StringUtils.isBlank(password)) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
    }

    // 2. 加密密码
    String encryptPassword = DigestUtil.md5Hex(SALT + password);

    // 3. 查询用户
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("student_number", studentNumber);
    queryWrapper.eq("password", encryptPassword);
    User user = this.baseMapper.selectOne(queryWrapper);

    // 4. 用户不存在或密码错误
    if (user == null) {
      log.info("user login failed, studentNumber cannot match password");
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "学号或密码错误");
    }

    // 5. 返回脱敏后的用户信息
    return getUserVO(user);
  }

  @Override
  public UserVO getLoginUser(Integer userId) {
    if (userId == null) {
      throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
    }

    User user = this.getById(userId);
    if (user == null) {
      throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
    }

    return getUserVO(user);
  }

  @Override
  public UserVO getUserVO(User user) {
    if (user == null) {
      return null;
    }

    UserVO userVO = new UserVO();
    BeanUtil.copyProperties(user, userVO);
    return userVO;
  }

  @Override
  public boolean verifyCaptcha(String email, String captcha) {
    if (StringUtils.isBlank(email) || StringUtils.isBlank(captcha)) {
      return false;
    }

    String redisKey = "email:code:" + email;
    Object value = redisTemplate.opsForValue().get(redisKey);

    if (value == null) {
      return false;
    }

    String savedCaptcha = value.toString();
    boolean result = captcha.equals(savedCaptcha);

    if (result) {
      // 验证成功后删除验证码
      redisTemplate.delete(redisKey);
    }

    return result;
  }

  @Override
  public String forgetPasswordStep1(ForgetPasswordStep1Request request) {
    if (request == null) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR);
    }

    String studentNumber = request.getStudentNumber();
    String email = request.getEmail();
    String captcha = request.getCaptcha();

    // 1. 校验参数
    if (StringUtils.isBlank(studentNumber) || StringUtils.isBlank(email) || StringUtils.isBlank(captcha)) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
    }

    // 2. 验证邮箱验证码
    if (!verifyCaptcha(email, captcha)) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误或已过期");
    }

    // 3. 查询用户是否存在
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("student_number", studentNumber);
    queryWrapper.eq("email", email);
    User user = this.baseMapper.selectOne(queryWrapper);

    if (user == null) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "学号或邮箱不匹配");
    }

    // 4. 生成重置令牌并存入Redis
    String resetToken = DigestUtil.md5Hex(SALT + studentNumber + System.currentTimeMillis());
    String redisKey = "user:reset:" + studentNumber;

    // 设置令牌有效期为30分钟
    redisTemplate.opsForValue().set(redisKey, resetToken, 30, TimeUnit.MINUTES);

    return resetToken;
  }

  @Override
  public boolean forgetPasswordStep2(ForgetPasswordStep2Request request) {
    if (request == null) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR);
    }

    String studentNumber = request.getStudentNumber();
    String newPassword = request.getNewPassword();
    String checkPassword = request.getCheckPassword();
    String resetToken = request.getResetToken();

    // 1. 校验参数
    if (StringUtils.isBlank(studentNumber) || StringUtils.isBlank(newPassword)
        || StringUtils.isBlank(checkPassword) || StringUtils.isBlank(resetToken)) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
    }

    // 2. 校验密码
    if (newPassword.length() < 8) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度不能小于8位");
    }

    if (!newPassword.equals(checkPassword)) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
    }

    // 3. 验证重置令牌
    String redisKey = "user:reset:" + studentNumber;
    Object value = redisTemplate.opsForValue().get(redisKey);

    if (value == null) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "重置令牌已过期");
    }

    String savedToken = value.toString();
    if (!resetToken.equals(savedToken)) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "重置令牌无效");
    }

    // 4. 更新密码
    String encryptPassword = DigestUtil.md5Hex(SALT + newPassword);

    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper.eq("student_number", studentNumber);
    updateWrapper.set("password", encryptPassword);

    boolean result = this.update(updateWrapper);

    if (result) {
      // 更新成功后删除重置令牌
      redisTemplate.delete(redisKey);
    }

    return result;
  }

  @Override
  public UserStatsVO getUserStats(Integer userId) {
    // 验证用户是否存在
    User user = userMapper.selectById(userId);
    if (user == null) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在");
    }

    UserStatsVO statsVO = new UserStatsVO();

    // 设置用户基本信息
    UserVO userVO = getUserVO(user);
    statsVO.setUserInfo(userVO);

    // 查询伤病报告数量
    QueryWrapper<InjuryReport> injuryReportQueryWrapper = new QueryWrapper<>();
    injuryReportQueryWrapper.eq("user_id", userId);
    Long injuryReportCount = injuryReportMapper.selectCount(injuryReportQueryWrapper);
    statsVO.setInjuryReportCount(injuryReportCount.intValue());

    // 查询康复计划数量
    QueryWrapper<RehabilitationPlan> planQueryWrapper = new QueryWrapper<>();
    planQueryWrapper.eq("patient_id", userId);
    Long planCount = rehabilitationPlanMapper.selectCount(planQueryWrapper);
    statsVO.setRehabilitationPlanCount(planCount.intValue());

    // 查询已完成的康复计划数量
    QueryWrapper<RehabilitationPlan> completedPlanQueryWrapper = new QueryWrapper<>();
    completedPlanQueryWrapper.eq("patient_id", userId)
        .eq("status", 2); // 假设2表示已完成
    Long completedPlanCount = rehabilitationPlanMapper.selectCount(completedPlanQueryWrapper);
    statsVO.setCompletedPlanCount(completedPlanCount.intValue());

    // 查询正在进行的康复计划数量
    QueryWrapper<RehabilitationPlan> activePlanQueryWrapper = new QueryWrapper<>();
    activePlanQueryWrapper.eq("patient_id", userId)
        .eq("status", 1); // 假设1表示进行中
    Long activePlanCount = rehabilitationPlanMapper.selectCount(activePlanQueryWrapper);
    statsVO.setActivePlanCount(activePlanCount.intValue());

    // 查询线上评估预约数量
    QueryWrapper<OnlineAssessmentAppointment> onlineAppointmentQueryWrapper = new QueryWrapper<>();
    onlineAppointmentQueryWrapper.eq("student_id", userId);
    Long onlineAppointmentCount = onlineAssessmentAppointmentMapper.selectCount(onlineAppointmentQueryWrapper);
    statsVO.setOnlineAppointmentCount(onlineAppointmentCount.intValue());

    // 查询线下评估预约数量
    QueryWrapper<OfflineAssessmentAppointment> offlineAppointmentQueryWrapper = new QueryWrapper<>();
    offlineAppointmentQueryWrapper.eq("student_id", userId);
    Long offlineAppointmentCount = offlineAssessmentAppointmentMapper.selectCount(offlineAppointmentQueryWrapper);
    statsVO.setOfflineAppointmentCount(offlineAppointmentCount.intValue());

    // 计算康复总天数 (通过计算每个计划的开始日期到结束日期的天数)
    int totalDays = 0;
    List<RehabilitationPlan> plans = rehabilitationPlanMapper.selectList(planQueryWrapper);
    for (RehabilitationPlan plan : plans) {
      LocalDate startDate = plan.getStartDate();
      LocalDate endDate = plan.getEndDate();
      if (startDate != null && endDate != null) {
        // 计算日期差
        long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate);
        totalDays += daysBetween;
      }
    }
    statsVO.setTotalRehabDays(totalDays);

    // 计算康复完成率
    int completionRate = 0;
    if (planCount > 0) {
      completionRate = (completedPlanCount.intValue() * 100) / planCount.intValue();
    }
    statsVO.setRehabCompletionRate(completionRate);

    return statsVO;
  }

  @Override
  public UserComprehensiveInfoVO getUserComprehensiveInfo(Integer userId) {
    // 验证用户是否存在
    User user = userMapper.selectById(userId);
    if (user == null) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在");
    }

    UserComprehensiveInfoVO comprehensiveInfo = new UserComprehensiveInfoVO();

    // 1. 设置基本信息
    BeanUtils.copyProperties(user, comprehensiveInfo);
    comprehensiveInfo.setRole(user.getRole());

    // 2. 获取用户统计信息
    UserStatsVO userStats = this.getUserStats(userId);
    comprehensiveInfo.setInjuryReportCount(userStats.getInjuryReportCount());
    comprehensiveInfo.setRehabilitationPlanCount(userStats.getRehabilitationPlanCount());

    // 处理进行中和已完成的康复计划数量
    // 使用QueryWrapper直接查询不同状态的康复计划数量
    QueryWrapper<RehabilitationPlan> ongoingWrapper = new QueryWrapper<>();
    ongoingWrapper.eq("patient_id", userId).eq("status", 0);
    Long ongoingCount = rehabilitationPlanMapper.selectCount(ongoingWrapper);

    QueryWrapper<RehabilitationPlan> completedWrapper = new QueryWrapper<>();
    completedWrapper.eq("patient_id", userId).eq("status", 1);
    Long completedCount = rehabilitationPlanMapper.selectCount(completedWrapper);

    comprehensiveInfo.setOngoingRehabilitationPlanCount(ongoingCount.intValue());
    comprehensiveInfo.setCompletedRehabilitationPlanCount(completedCount.intValue());

    // 3. 获取最近的伤病报告（最近5条）
    QueryWrapper<InjuryReport> injuryWrapper = new QueryWrapper<>();
    injuryWrapper.eq("user_id", userId)
        .orderByDesc("created_at")
        .last("limit 5");
    List<InjuryReport> recentInjuries = injuryReportMapper.selectList(injuryWrapper);

    // 将实体转为VO
    List<InjuryReportVO> recentInjuryVOs = new ArrayList<>();
    if (recentInjuries != null && !recentInjuries.isEmpty()) {
      for (InjuryReport injury : recentInjuries) {
        InjuryReportVO vo = new InjuryReportVO();
        BeanUtils.copyProperties(injury, vo);
        recentInjuryVOs.add(vo);
      }
    }
    comprehensiveInfo.setRecentInjuryReports(recentInjuryVOs);

    // 4. 获取最近的康复计划（最近5条）
    QueryWrapper<RehabilitationPlan> planWrapper = new QueryWrapper<>();
    planWrapper.eq("patient_id", userId)
        .orderByDesc("created_at")
        .last("limit 5");
    List<RehabilitationPlan> recentPlans = rehabilitationPlanMapper.selectList(planWrapper);

    // 将实体转为VO
    List<RehabilitationPlanVO> recentPlanVOs = new ArrayList<>();
    if (recentPlans != null && !recentPlans.isEmpty()) {
      for (RehabilitationPlan plan : recentPlans) {
        RehabilitationPlanVO vo = new RehabilitationPlanVO();
        BeanUtils.copyProperties(plan, vo);
        recentPlanVOs.add(vo);
      }
    }
    comprehensiveInfo.setRecentRehabilitationPlans(recentPlanVOs);

    // 5. 设置其他统计信息
    // 默认设置为0
    comprehensiveInfo.setAssessmentCount(0);
    comprehensiveInfo.setHealthGoalCount(0);
    comprehensiveInfo.setExerciseRecordCount(0);

    return comprehensiveInfo;
  }

  /**
   * 更新用户信息
   *
   * @param userId        用户ID
   * @param updateRequest 更新请求
   * @return 是否更新成功
   */
  @Override
  @Transactional
  public boolean updateUserInfo(Integer userId, UserUpdateInfoRequest updateRequest) {
    // 校验参数
    if (userId == null || updateRequest == null) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR);
    }

    // 检查用户是否存在
    User user = getById(userId);
    if (user == null) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在");
    }

    // 创建更新条件
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper.eq("id", userId);
    boolean needUpdate = false;

    // 更新用户名
    if (StringUtils.isNotBlank(updateRequest.getUsername())) {
      updateWrapper.set("username", updateRequest.getUsername());
      needUpdate = true;
    }

    // 更新邮箱（检查唯一性）
    if (StringUtils.isNotBlank(updateRequest.getEmail())) {
      String newEmail = updateRequest.getEmail();
      // 只有当邮箱变更时才检查唯一性
      if (!newEmail.equals(user.getEmail())) {
        // 检查新邮箱是否已被其他用户使用
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("email", newEmail).ne("id", userId);
        Long count = baseMapper.selectCount(queryWrapper);
        if (count > 0) {
          throw new BusinessException(ErrorCode.PARAMS_ERROR, "该邮箱已被其他用户使用");
        }
        updateWrapper.set("email", newEmail);
        needUpdate = true;
      }
    }

    // 更新手机号（检查唯一性）
    if (StringUtils.isNotBlank(updateRequest.getPhone())) {
      String newPhone = updateRequest.getPhone();
      // 只有当手机号变更时才检查唯一性
      if (!newPhone.equals(user.getPhone())) {
        // 检查新手机号是否已被其他用户使用
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", newPhone).ne("id", userId);
        Long count = baseMapper.selectCount(queryWrapper);
        if (count > 0) {
          throw new BusinessException(ErrorCode.PARAMS_ERROR, "该手机号已被其他用户使用");
        }
        updateWrapper.set("phone", newPhone);
        needUpdate = true;
      }
    }

    // 更新密码
    if (StringUtils.isNotBlank(updateRequest.getOldPassword())
        && StringUtils.isNotBlank(updateRequest.getNewPassword())
        && StringUtils.isNotBlank(updateRequest.getConfirmPassword())) {

      // 校验新密码与确认密码是否一致
      if (!updateRequest.getNewPassword().equals(updateRequest.getConfirmPassword())) {
        throw new BusinessException(ErrorCode.PARAMS_ERROR, "新密码与确认密码不一致");
      }

      // 校验新密码长度
      if (updateRequest.getNewPassword().length() < 8) {
        throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度不能小于8位");
      }

      // 校验旧密码是否正确
      String encryptOldPassword = DigestUtil.md5Hex(SALT + updateRequest.getOldPassword());
      if (!encryptOldPassword.equals(user.getPassword())) {
        throw new BusinessException(ErrorCode.PARAMS_ERROR, "旧密码不正确");
      }

      // 加密新密码
      String encryptNewPassword = DigestUtil.md5Hex(SALT + updateRequest.getNewPassword());
      updateWrapper.set("password", encryptNewPassword);
      needUpdate = true;
    }

    // 只有当有字段需要更新时才执行更新操作
    if (needUpdate) {
      return update(updateWrapper);
    }

    return true;
  }
}